package com.hui.admin.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hui.admin.common.enums.ErrorCodeEnum;
import com.hui.admin.common.exception.UserException;
import com.hui.admin.dto.request.*;
import com.hui.admin.dto.response.UserForgetPasswordResponseDTO;
import com.hui.admin.dto.response.UserListDTO;
import com.hui.admin.dto.response.UserLoginResponseDTO;
import com.hui.admin.dto.response.UserPageResponseDTO;
import com.hui.admin.entity.UserDO;
import com.hui.admin.entity.UserProfileDO;
import com.hui.admin.mapper.UserMapper;
import com.hui.admin.service.UserProfileService;
import com.hui.admin.service.UserService;
import com.hui.admin.utils.BcryptUtil;
import com.hui.admin.utils.EmailUtil;
import com.hui.admin.utils.JwtUtil;
import com.hui.admin.utils.PhoneUtil;
import com.hui.admin.utils.redis.RedisCaptchaUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.List;

/**
 * 用户服务实现层
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {

    @Value("${app.password}")
    private String defaultPassword;

    @Resource
    private UserProfileService userProfileService;

    @Override
    public void deleteUserById(Long id) {
        boolean flag = removeById(id);

        if (!flag) {
            throw new UserException(ErrorCodeEnum.USERNAME_NOT_EXISTS);
        }
    }

    @Override
    public void addUser(UserSaveRequestDTO userSaveRequestDTO) {
        // 检查用户名唯一
        if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, userSaveRequestDTO.getUsername())) > 0) {
            throw new UserException(ErrorCodeEnum.USERNAME_EXISTS);
        }

        // 检查手机号唯一
        if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getPhone, userSaveRequestDTO.getPhone())) > 0) {
            throw new UserException(ErrorCodeEnum.PHONE_EXISTS);
        }

        // 检查邮箱唯一
        if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getEmail, userSaveRequestDTO.getEmail())) > 0) {
            throw new UserException(ErrorCodeEnum.EMAIL_EXISTS);
        }

        // 构造用户实体
        UserDO result = BeanUtil.copyProperties(userSaveRequestDTO, UserDO.class);

        // 密码加密
        result.setPassword(BcryptUtil.hashPassword(defaultPassword));

        // 新增数据
        save(result);

        // 新增用户信息
        UserProfileDO profile = new UserProfileDO();
        profile.setUserId(result.getId());
        profile.setName(result.getName());
        userProfileService.save(profile);
    }

    @Override
    public void updateUser(UserSaveRequestDTO requestDTO) {
        UserDO currentUser = getById(requestDTO.getId());

        if (currentUser == null) {
            throw new UserException(ErrorCodeEnum.USER_NOT_EXISTS);
        }

        // 检查用户名是否更改，并验证唯一性
        if (!currentUser.getUsername().equals(requestDTO.getUsername())) {
            if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, requestDTO.getUsername())) > 0) {
                throw new UserException(ErrorCodeEnum.USERNAME_EXISTS);
            }
        }

        // 检查手机号是否更改，并验证唯一性
        if (!currentUser.getPhone().equals(requestDTO.getPhone())) {
            if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getPhone, requestDTO.getPhone())) > 0) {
                throw new UserException(ErrorCodeEnum.PHONE_EXISTS);
            }
        }

        // 检查邮箱是否更改，并验证唯一性
        if (!currentUser.getEmail().equals(requestDTO.getEmail())) {
            if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getEmail, requestDTO.getEmail())) > 0) {
                throw new UserException(ErrorCodeEnum.EMAIL_EXISTS);
            }
        }

        LambdaUpdateWrapper<UserDO> updateWrapper = new LambdaUpdateWrapper<>();

        updateWrapper.eq(UserDO::getId, requestDTO.getId())
                .set(UserDO::getName, requestDTO.getName())
                .set(UserDO::getUsername, requestDTO.getUsername())
                .set(UserDO::getPhone, requestDTO.getPhone())
                .set(UserDO::getEmail, requestDTO.getEmail())
                .set(UserDO::getStatus, requestDTO.getStatus())
                .set(UserDO::getAvatar, requestDTO.getAvatar())
                .set(UserDO::getUserType, requestDTO.getUserType());

        boolean flag = update(updateWrapper);

        if (!flag) {
            throw new UserException(ErrorCodeEnum.USERNAME_NOT_EXISTS);
        }
    }

    @Override
    public UserPageResponseDTO getUserPage(UserPageRequestDTO requestDTO) {
        Page<Map<String, Object>> userPage = new Page<>(requestDTO.getPageNum(), requestDTO.getPageSize());

        LambdaQueryWrapper<UserDO> queryWrapper = checkUnique(requestDTO);

        queryWrapper.orderByDesc(UserDO::getId)
                .select(UserDO::getId,
                        UserDO::getName,
                        UserDO::getUsername,
                        UserDO::getEmail,
                        UserDO::getPhone,
                        UserDO::getAvatar,
                        UserDO::getStatus,
                        UserDO::getUserType);

        Page<Map<String, Object>> result = baseMapper.selectMapsPage(userPage, queryWrapper);

        List<UserListDTO> list = result.getRecords().stream()
                .map(map -> {
                    UserListDTO dto = new UserListDTO();
                    dto.setId((Long) map.getOrDefault("id", 0L));
                    dto.setName((String) map.getOrDefault("name", ""));
                    dto.setUsername((String) map.getOrDefault("username", ""));
                    dto.setEmail((String) map.getOrDefault("email", ""));
                    dto.setPhone((String) map.getOrDefault("phone", ""));
                    dto.setAvatar((String) map.getOrDefault("avatar", ""));
                    dto.setStatus((Integer) map.getOrDefault("status", 0));
                    dto.setUserType((Integer) map.getOrDefault("user_type", 0));
                    return dto;
                })
                .toList();

        long total = result.getTotal();

        return new UserPageResponseDTO(list, total);
    }

    @Override
    public void updateUserStatus(String username, Integer status) {
        LambdaUpdateWrapper<UserDO> updateWrapper = new LambdaUpdateWrapper<>();

        updateWrapper.eq(UserDO::getUsername, username)
                .set(UserDO::getStatus, status);

        boolean flag = update(updateWrapper);

        if (!flag) {
            throw new UserException(ErrorCodeEnum.USER_NOT_EXISTS);
        }
    }

    @Override
    public void deleteUsers(Long[] ids) {
        if ((ArrayUtil.isEmpty(ids))) {
            throw new UserException(ErrorCodeEnum.INVALID_PARAMS);
        }

        boolean flag = removeByIds(Arrays.asList(ids));

        if (!flag) {
            throw new UserException(ErrorCodeEnum.USER_NOT_EXISTS);
        }
    }

    @Override
    public void exportUsers(HttpServletResponse response, UserExportRequestDTO requestDTO) {
        try {
            LambdaQueryWrapper<UserDO> queryWrapper = checkUnique(requestDTO);

            if (CollUtil.isNotEmpty(requestDTO.getIds())) {
                queryWrapper.in(UserDO::getId, requestDTO.getIds());
            }

            List<UserDO> list = list(queryWrapper);

            ExcelWriter writer = ExcelUtil.getWriter(true);

            // 设置标题别名
            writer.addHeaderAlias("username", "用户名");
            writer.addHeaderAlias("name", "姓名");
            writer.addHeaderAlias("phone", "手机号");
            writer.addHeaderAlias("email", "邮箱");
            writer.addHeaderAlias("avatar", "头像");
            writer.addHeaderAlias("userType", "角色");
            writer.addHeaderAlias("status", "状态");

            // 只导出设置过标题的列
            writer.setOnlyAlias(true);

            // 写入数据
            writer.write(list, true);

            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");

            try (ServletOutputStream out = response.getOutputStream()) {
                writer.flush(out, true);
            }

            writer.close();

        } catch (IOException e) {
            throw new RuntimeException("导出失败", e);
        }
    }

    @Transactional
    @Override
    public void importUsers(MultipartFile file) {
        try (InputStream is = file.getInputStream();
             ExcelReader reader = ExcelUtil.getReader(is)) {

            reader.addHeaderAlias("用户名", "username");
            reader.addHeaderAlias("姓名", "name");
            reader.addHeaderAlias("手机号", "phone");
            reader.addHeaderAlias("邮箱", "email");
            reader.addHeaderAlias("头像", "avatar");
            reader.addHeaderAlias("角色", "userType");
            reader.addHeaderAlias("状态", "status");

            List<UserDO> list = reader.readAll(UserDO.class);

            // 检查导入数据的唯一性
            Set<String> usernames = new HashSet<>();
            Set<String> phones = new HashSet<>();
            Set<String> emails = new HashSet<>();

            for (UserDO user : list) {
                if (usernames.contains(user.getUsername())) {
                    throw new UserException(ErrorCodeEnum.USERNAME_EXISTS);
                }
                if (phones.contains(user.getPhone())) {
                    throw new UserException(ErrorCodeEnum.PHONE_EXISTS);
                }
                if (emails.contains(user.getEmail())) {
                    throw new UserException(ErrorCodeEnum.EMAIL_EXISTS);
                }

                usernames.add(user.getUsername());
                phones.add(user.getPhone());
                emails.add(user.getEmail());
            }

            // 检查与数据库中已存在的数据是否冲突
            List<String> existingUsernames = baseMapper.selectObjs(new LambdaQueryWrapper<UserDO>()
                    .in(UserDO::getUsername, usernames)
                    .select(UserDO::getUsername));

            if (!existingUsernames.isEmpty()) {
                throw new UserException(ErrorCodeEnum.USERNAME_EXISTS);
            }

            List<String> existingPhones = baseMapper.selectObjs(new LambdaQueryWrapper<UserDO>()
                    .in(UserDO::getPhone, phones)
                    .select(UserDO::getPhone));

            if (!existingPhones.isEmpty()) {
                throw new UserException(ErrorCodeEnum.PHONE_EXISTS);
            }

            List<String> existingEmails = baseMapper.selectObjs(new LambdaQueryWrapper<UserDO>()
                    .in(UserDO::getEmail, emails)
                    .select(UserDO::getEmail));

            if (!existingEmails.isEmpty()) {
                throw new UserException(ErrorCodeEnum.EMAIL_EXISTS);
            }

            // 设置加密密码
            for (UserDO user : list) {
                user.setPassword(BcryptUtil.hashPassword(defaultPassword));
            }

            saveBatch(list);

        } catch (IOException e) {
            throw new RuntimeException("导入失败", e);
        }
    }

    @Override
    public UserLoginResponseDTO login(UserLoginRequestDTO request) {

        String storedCode = RedisCaptchaUtil.getCaptcha(request.getVerifyCodeUuid());

        if (storedCode == null) {
            throw new UserException(ErrorCodeEnum.CAPTCHA_EXPIRED); // 验证码过期
        }

        if (!storedCode.equalsIgnoreCase(request.getVerifyCode())) {
            throw new UserException(ErrorCodeEnum.CAPTCHA_MISMATCH); // 验证码错误
        }

        // 校验通过后删除验证码
        RedisCaptchaUtil.deleteCaptcha(request.getVerifyCodeUuid());

        // 查询数据
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();

        if (EmailUtil.isValidEmail(request.getUsername())) {
            // 邮箱
            queryWrapper.eq(UserDO::getEmail, request.getUsername());
        } else if (PhoneUtil.isValidPhone(request.getUsername())) {
            // 手机号
            queryWrapper.eq(UserDO::getPhone, request.getUsername());
        } else {
            // 用户名（默认）
            queryWrapper.eq(UserDO::getUsername, request.getUsername());
        }

        UserDO userDO = baseMapper.selectOne(queryWrapper);

        if (userDO == null) {
            throw new UserException(ErrorCodeEnum.USER_NOT_EXISTS);
        }

        // 校验密码
        if (!BcryptUtil.checkPassword(request.getPassword(), userDO.getPassword())) {
            throw new UserException(ErrorCodeEnum.PASSWORD_ERROR);
        }

        // 用户是否可用
        if (userDO.getStatus() == 0) {
            throw new UserException(ErrorCodeEnum.USER_DISABLED);
        }

        // 构造返回对象并设置 Token
        UserLoginResponseDTO dto = BeanUtil.copyProperties(userDO, UserLoginResponseDTO.class);
        dto.setToken(JwtUtil.generateToken(request.getUsername()));

        return dto;
    }

    @Override
    public void getShearCaptcha(HttpServletResponse response) {
        String uuid = UUID.randomUUID().toString();

        try {
            // 创建验证码对象并设置参数
            ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(150, 40, 4, 3);
            captcha.setBackground(new Color(250, 250, 220));

            // 存储 uuid 和 code 进 Redis
            RedisCaptchaUtil.setCaptcha(uuid, captcha.getCode(), 300L);

            // 设置响应头
            response.setContentType("image/png");
            response.setHeader("verifycodeuuid", uuid);
            response.setHeader("Access-Control-Expose-Headers", "verifycodeuuid");


            try (OutputStream os = response.getOutputStream()) {
                captcha.write(os);
            }

        } catch (IOException e) {
            RedisCaptchaUtil.deleteCaptcha(uuid);
            throw new UserException(ErrorCodeEnum.CREATE_CAPTCHA_ERROR);
        }
    }

    @Override
    public void register(UserRegisterRequestDTO request) {
        // 检查是否同意协议
        if (!request.getAgreement()) {
            throw new UserException(ErrorCodeEnum.AGREEMENT_NOT_ACCEPTED);
        }

        // 检查用户名是否存在
        if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, request.getUsername())) > 0) {
            throw new UserException(ErrorCodeEnum.USERNAME_EXISTS);
        }

        // 检查手机号是否存在
        if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getPhone, request.getUsername())) > 0) {
            throw new UserException(ErrorCodeEnum.PHONE_EXISTS);
        }

        // 检查邮箱是否存在
        if (count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getEmail, request.getUsername())) > 0) {
            throw new UserException(ErrorCodeEnum.EMAIL_EXISTS);
        }

        // 构造用户实体
        UserDO userDO = new UserDO();
        userDO.setName(request.getName());
        userDO.setUsername(request.getUsername());
        userDO.setPassword(BcryptUtil.hashPassword(request.getPassword()));
        userDO.setPhone(request.getPhone());
        userDO.setEmail(request.getEmail());
        userDO.setStatus(1);
        userDO.setUserType(1);

        // 插入用户数据
        save(userDO);

        // 插入用户信息
        UserProfileDO userProfileDO = new UserProfileDO();
        userProfileDO.setUserId(userDO.getId());
        userProfileDO.setName(userDO.getName());
        userProfileService.save(userProfileDO);
    }

    @Override
    public UserForgetPasswordResponseDTO forgetPassword(UserForgetPasswordRequestDTO request) {
        // 检查请求参数是否为空
        if (StrUtil.isEmpty(request.getAccount())) {
            throw new UserException(ErrorCodeEnum.INVALID_PARAMS);
        }

        // 查询用户是否存储
        UserDO userDO = checkExist(request);

        if (userDO == null) {
            throw new UserException(ErrorCodeEnum.USER_NOT_EXISTS);
        }

        UserForgetPasswordResponseDTO result = new UserForgetPasswordResponseDTO();
        result.setName(userDO.getName());
        result.setUsername(userDO.getUsername());

        return result;
    }

    @Override
    public void forgetPasswordConfirm(UserForgetPasswordRequestDTO request) {
        // 校验请求参数
        if (StrUtil.isEmpty(request.getAccount()) || StrUtil.isEmpty(request.getPassword()) || StrUtil.isEmpty(request.getNewPassword())) {
            throw new UserException(ErrorCodeEnum.INVALID_PARAMS);
        }

        // 查询用户是否存储
        UserDO userDO = checkExist(request);

        if (userDO == null) {
            throw new UserException(ErrorCodeEnum.USER_NOT_EXISTS);
        }

        // 更新密码
        String newPasswordHash = BcryptUtil.hashPassword(request.getNewPassword());

        LambdaUpdateWrapper<UserDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserDO::getId, userDO.getId())
                .set(UserDO::getPassword, newPasswordHash);

        boolean flag = update(updateWrapper);

        if (!flag) {
            throw new UserException(ErrorCodeEnum.PASSWORD_RESET_FAILED);
        }
    }

    @Override
    public void updateUserPassword(UserChangePasswordRequestDTO requestDTO) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserDO::getId, requestDTO.getId());
        UserDO one = getOne(queryWrapper);

        // 判断密码是否正确
        if (!BcryptUtil.checkPassword(requestDTO.getPassword(), one.getPassword())) {
            throw new UserException(ErrorCodeEnum.PASSWORD_ERROR);
        }

        // 判断原密码和新密码是否一致
        if (requestDTO.getPassword().equals(requestDTO.getNewPassword())) {
            throw new UserException(ErrorCodeEnum.PASSWORD_NOT_CHANGE);
        }

        // 判断新密码和确认密码是否一致
        if (!requestDTO.getNewPassword().equals(requestDTO.getConfirmPassword())) {
            throw new UserException(ErrorCodeEnum.PASSWORD_NOT_MATCH);
        }

        // 加密新密码，更新实体
        String newPasswordHash = BcryptUtil.hashPassword(requestDTO.getNewPassword());
        one.setPassword(newPasswordHash);

        update(one, queryWrapper);
    }

    @Override
    public Long getUserIdByUsername(String username) {
        return count(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, username)) > 0 ? getOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, username)).getId() : 0L;
    }

    /**
     * 检查用户名 / 邮箱 / 手机号 是否存在
     */
    private UserDO checkExist(UserForgetPasswordRequestDTO request) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();

        if (EmailUtil.isValidEmail(request.getAccount())) {
            queryWrapper.eq(UserDO::getEmail, request.getAccount());
        } else if (PhoneUtil.isValidPhone(request.getAccount())) {
            queryWrapper.eq(UserDO::getPhone, request.getAccount());
        } else {
            queryWrapper.eq(UserDO::getUsername, request.getAccount());
        }

        return baseMapper.selectOne(queryWrapper);
    }



    /**
     * 检查 用户名、手机号、邮箱 是否唯一
     */
    private LambdaQueryWrapper<UserDO> checkUnique(UserQueryCondition requestDTO) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>();

        if (requestDTO.getUsername() != null) {
            queryWrapper.like(UserDO::getUsername, requestDTO.getUsername());
        }

        if (requestDTO.getPhone() != null) {
            queryWrapper.like(UserDO::getPhone, requestDTO.getPhone());
        }

        if (requestDTO.getEmail() != null) {
            queryWrapper.like(UserDO::getEmail, requestDTO.getEmail());
        }

        return queryWrapper;
    }
}


